#include <Lucnx/interrupt.h>
#include <Lucnx/console.h>
#include <Lucnx/stdlib.h>
#include <Lucnx/printk.h>
#include <Lucnx/debug.h>
#include <Lucnx/mutex.h>
#include <Lucnx/fifo.h>
#include <Lucnx/device.h>
#include <Lucnx/io.h>

char keytable0[0x80] = {
    0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08, 0,
    'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0x0a, 0, 'a', 's',
    'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v',
    'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
    '2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x5c, 0, 0};

char keytable1[0x80] = {
    0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0x08, 0,
    'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0x0a, 0, 'A', 'S',
    'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
    'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
    '2', '3', '0', '.', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, '_', 0, 0, 0, 0, 0, 0, 0, 0, 0, '|', 0, 0};

#define L_SHFT 0x2a
#define R_SHFT 0x36
#define CAPS 0x3a

static fifo_t fifo;
static lock_t lock;
static task_t *waiter;

bool debug_mode;
bool is_caps_lock;
bool is_shift;
bool ext;

void keyboard_handler()
{
    outb(0x20, 0x61);     // 通知 8259A 中断已受理
    u16 data = inb(0x60); // 获取输入的扫描码

    if (debug_mode)
    {
        printk("Input: 0x%x\n", data);
    }

    if (data == 0xe0)
    {
        ext = true;
        return;
    }
    
    if (ext)
    {
        data |= 0xe000;
        ext = false;
    }

    if (data & 0x0080)
    {
        u16 make = data & 0xFF7F; 
        if (make == L_SHFT || make == R_SHFT)
        {
            is_shift = false;
        }
        return;
    }
    else
    {
        if (data == CAPS)
        {
            is_caps_lock = !is_caps_lock;
            return;
        }
        if (data == L_SHFT || data == R_SHFT)
        {
            is_shift = true;
            return;
        }

        char *keymap;

        if (is_shift || is_caps_lock)
        {
            keymap = keytable1;
        }
        else
        {
            keymap = keytable0;
        }

        if (data & 0xE000)
        {
            return;
        }

        if (keymap[data])
        {
            fifo_put(&fifo, keymap[data]);
        }

        if (waiter != NULL)
        {
            task_unblock(waiter);
        }
    }
}

size_t keyboard_read(void *dev, char *buf, size_t count)
{
    char *ptr = buf;

    for (size_t i = 0; i < count;)
    {
        if (fifo_empty(&fifo))
        {
            waiter = running_task();
            task_block(waiter, TASK_BLOCKED);
        }
        char ch = fifo_get(&fifo);
        ptr[i++] = ch;
    }
    return count;
}

void keyboard_init()
{
    debug_mode = false;
    ext = false;
    is_caps_lock = false;
    is_shift = false;

    waiter = NULL;

    fifo_init(&fifo, 128);
    set_pic_irq(0x21, true);
    register_handler(0x21, keyboard_handler);

    device_install("keyboard", NULL, keyboard_read, NULL, NULL, DEV_CHAR, DF_READ);
}